using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Mail;
using Decav.Data;
using System.Xml.Serialization;
using System.Xml;
using System.Runtime.Serialization;
using System.IO;

namespace Decav.Reality
{
    /// <summary>
    /// Contact information fro a person or company.
    /// </summary>
    [Serializable]
    public class Contact : IXmlSerializable
    {
        /// <summary>
        /// Initializes a new <see cref="Contact"/> with a new identifier.
        /// </summary>
        public Contact()
        {
            _id = Guid.NewGuid();
        }

        /// <summary>
        /// Initializes the <see cref="Contact"/> from a <see cref="DataRecordAdapter"/>.
        /// </summary>
        /// <param name="adapt">The data record adapter to intialize from.</param>
        protected Contact(DataRecordAdapter adapt)
        {
            _id = adapt.HasValue("Id") ? adapt.GetGuid("Id") : Guid.Empty;
            
            _firstName = adapt.GetString("FirstName");
            _middleName = adapt.GetString("MiddleName");
            _lastName = adapt.GetString("LastName");

            _company = adapt.GetString("Company");
            _title = adapt.GetString("Title");
            
            _mobilePhone = adapt.GetPhoneNumber("MobilePhone");
            _homePhone = adapt.GetPhoneNumber("HomePhone");
            _workPhone = adapt.GetPhoneNumber("WorkPhone");
            _faxPhone = adapt.GetPhoneNumber("FaxPhone");
            
            _homeAddress = adapt.GetStreetAddress("Home_");
            _workAddress = adapt.GetStreetAddress("Work_");

            _workEmail = adapt.GetEmailAddress("WorkEmail");
            _personalEmail = adapt.GetEmailAddress("PersonalEmail");

            _birthday = adapt.HasValue("Birthday") ? adapt.GetDateTime("Birthday") : DateTime.MinValue;
        }

        /// <summary>
        /// Initializes the <see cref="Contact"/> from an XML reader.
        /// </summary>
        /// <param name="reader">The XML reader to initalize from.</param>
        protected Contact(XmlReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            ((IXmlSerializable)this).ReadXml(reader);
        }

        #region Fields
        private Guid _id;
        private string _firstName;
        private string _middleName;
        private string _lastName;
        private string _company;
        private string _title;
        private PhoneNumber _mobilePhone;
        private PhoneNumber _homePhone;
        private PhoneNumber _workPhone;
        private PhoneNumber _faxPhone;
        private StreetAddress _homeAddress;
        private StreetAddress _workAddress;
        private EmailAddress _workEmail;
        private EmailAddress _personalEmail;
        private DateTime _birthday;
        #endregion

        #region Properties
        /// <summary>
        /// Gets the unique identifier of the contact.
        /// </summary>
        public Guid Id
        {
            get
            {
                return _id;
            }
            set
            {
                _id = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons first name.
        /// </summary>
        public string FirstName
        {
            get
            {
                return _firstName;
            }
            set
            {
                _firstName = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons middle name.
        /// </summary>
        public string MiddleName
        {
            get
            {
                return _middleName;
            }
            set
            {
                _middleName = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons last name.
        /// </summary>
        public string LastName
        {
            get
            {
                return _lastName;
            }
            set
            {
                _lastName = value;
            }
        }

        /// <summary>
        /// Gets or sets the company the person works for.
        /// </summary>
        public string Company
        {
            get
            {
                return _company;
            }
            set
            {
                _company = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons title (for example, the title at his company 'CEO').
        /// </summary>
        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons mobile telephone number.
        /// </summary>
        public PhoneNumber MobilePhone
        {
            get
            {
                return _mobilePhone;
            }
            set
            {
                _mobilePhone = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons home telephone number
        /// </summary>
        public PhoneNumber HomePhone
        {
            get
            {
                return _homePhone;
            }
            set
            {
                _homePhone = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons work telephone number.
        /// </summary>
        public PhoneNumber WorkPhone
        {
            get
            {
                return _workPhone;
            }
            set
            {
                _workPhone = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons facimile telephone number.
        /// </summary>
        public PhoneNumber FaxPhone
        {
            get
            {
                return _faxPhone;
            }
            set
            {
                _faxPhone = value;
            }
        }

        /// <summary>
        /// Gets the persons home address.
        /// </summary>
        public StreetAddress HomeAddress
        {
            get
            {
                if (_homeAddress == null)
                    _homeAddress = new StreetAddress();

                return _homeAddress;
            }
        }

        /// <summary>
        /// Gets the persons work address.
        /// </summary>
        public StreetAddress WorkAddress
        {
            get
            {
                if (_workAddress == null)
                    _workAddress = new StreetAddress();

                return _workAddress;
            }
        }

        /// <summary>
        /// Gets or sets the persons work email address.
        /// </summary>
        public EmailAddress WorkEmail
        {
            get
            {
                return _workEmail;
            }
            set
            {
                _workEmail = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons work personal address.
        /// </summary>
        public EmailAddress PersonalEmail
        {
            get
            {
                return _personalEmail;
            }
            set
            {
                _personalEmail = value;
            }
        }

        /// <summary>
        /// Gets or sets the persons birthday date.
        /// </summary>
        public DateTime Birthday
        {
            get
            {
                return _birthday;
            }
            set
            {
                _birthday = value;
            }
        }
        #endregion

        /// <summary>
        /// Gets the title of the contact.
        /// </summary>
        /// <returns>The title of the contact, generally by their <see cref="FirstName"/>
        /// and <see cref="LastName"/>.</returns>
        public override string ToString()
        {
            if (FirstName == null)
            {
                if (LastName == null)
                {
                    if (Company == null)
                        return string.Empty;
                    else
                        return Company;
                }
                else
                {
                    return LastName;
                }
            }

            if (LastName == null)
                return FirstName;

            return string.Format("{0}, {1}", LastName, FirstName);
        }

        /// <summary>
        /// Creates a new <see cref="Contact"/> from a contact XML string.
        /// </summary>
        /// <param name="xml">The XML defining the contact.</param>
        /// <returns>A new contact, as defined in the XML.</returns>
        public static Contact FromXml(string xml)
        {
            if (xml == null)
                throw new ArgumentNullException("xml");
        
            using(StringReader reader = new StringReader(xml))
            using (XmlReader xmlrdr = XmlReader.Create(reader))
            {
                return new Contact(xmlrdr);
            }
        }

        #region IXmlSerializable Members

        System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
        {
            reader.Read();
            if (reader.Name != "contact")
                throw new SerializationException("The contact did not start at XML node 'contact'.");

            _id = reader["id"] == null ? Guid.Empty : new Guid(reader["id"]);

            reader.ReadToDescendant("name");
            _firstName = reader["firstName"];
            _middleName = reader["middleName"];
            _lastName = reader["lastName"];
            
            reader.ReadToNextSibling("company");
            _company = reader["company"];
            _title = reader["title"];

            reader.ReadToNextSibling("phoneNumbers");
            _workPhone = reader["work"] != null ? new PhoneNumber(reader["work"]) : PhoneNumber.Empty;
            _homePhone = reader["home"] != null ? new PhoneNumber(reader["home"]) : PhoneNumber.Empty;
            _mobilePhone = reader["mobile"] != null ? new PhoneNumber(reader["mobile"]) : PhoneNumber.Empty;
            _faxPhone = reader["fax"] != null ? new PhoneNumber(reader["fax"]) : PhoneNumber.Empty;

            reader.ReadToNextSibling("email");
            _personalEmail = reader["personal"] == null ? EmailAddress.Empty : new EmailAddress(reader["personal"]);
            _workEmail = reader["work"] == null ? EmailAddress.Empty : new EmailAddress(reader["work"]);

            reader.ReadToNextSibling("workAddress");
            if (reader.ReadToDescendant("address"))
                _workAddress = XmlSerializationHelper.BuildObject<StreetAddress>(reader.ReadSubtree());
            reader.Read();

            reader.ReadToNextSibling("homeAddress");
            if (reader.ReadToDescendant("address"))
                _homeAddress = XmlSerializationHelper.BuildObject<StreetAddress>(reader.ReadSubtree());
            reader.Read();

            OnReadXml(reader);
        }

        void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("contact");

            if (_id != Guid.Empty)
                writer.WriteAttributeString("id", _id.ToString());

            writer.WriteStartElement("name");
            if (_firstName != null)
                writer.WriteAttributeString("firstName", _firstName);

            if (_middleName != null)
                writer.WriteAttributeString("middleName", _middleName);

            if (_lastName != null)
                writer.WriteAttributeString("lastName", _lastName);
            writer.WriteEndElement(); // name

            writer.WriteStartElement("company");
            if (_company != null)
                writer.WriteAttributeString("company", _company);

            if (_title != null)
                writer.WriteAttributeString("title", _title);
            writer.WriteEndElement(); // company

            writer.WriteStartElement("phoneNumbers");

            if (_workPhone != PhoneNumber.Empty)
                writer.WriteAttributeString("work", _workPhone.ToString());

            if (_homePhone != PhoneNumber.Empty)
                writer.WriteAttributeString("home", _homePhone.ToString());

            if (_mobilePhone != PhoneNumber.Empty)
                writer.WriteAttributeString("mobile", _mobilePhone.ToString());

            if (_faxPhone != PhoneNumber.Empty)
                writer.WriteAttributeString("fax", _faxPhone.ToString());

            writer.WriteEndElement(); // phoneNumbers

            writer.WriteStartElement("email");

            if (_workEmail != null)
                writer.WriteAttributeString("work", _workEmail.ToString());

            if (_personalEmail != null)
                writer.WriteAttributeString("personal", _personalEmail.ToString());

            writer.WriteEndElement(); // email

            writer.WriteStartElement("workAddress");

            if (_workAddress != null)
                ((IXmlSerializable)_workAddress).WriteXml(writer);

            writer.WriteEndElement(); // workAddress

            writer.WriteStartElement("homeAddress");

            if (_homeAddress != null)
                ((IXmlSerializable)_homeAddress).WriteXml(writer);

            writer.WriteEndElement(); // homeAddress

            OnWriteXml(writer);


            writer.WriteEndElement(); // contact
        }

        /// <summary>
        /// When overridden in a derrived class, allows the derrived class to serialize itself
        /// after the starting "contact" xml node.
        /// </summary>
        /// <param name="writer">The writer to write to.</param>
        protected virtual void OnWriteXml(XmlWriter writer)
        {
        }

        /// <summary>
        /// When overridden in a derrived class, allows the derrived class to deserialize
        /// itself from the contact XML.
        /// </summary>
        /// <param name="reader">The reader to read from.</param>
        protected virtual void OnReadXml(XmlReader reader)
        {
        }

        #endregion
    }
}
